package kuzmenkov.oip.common;

import org.springframework.context.MessageSource;

import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Class is responsible for method calling which depends on i18n
 * User: Michael Kuzmenkov
 * Date: 19.03.2009
 * Time: 9:55:59
 */
public class I18nTool {
    private final static Logger log = Logger.getLogger(I18nTool.class.getName());

//    static {
//        log.setLevel(Level.WARNING);
//    }

    private MessageSource messageSource;
    private Locale currentLocale = null;
//    private Locale storedLocale;

//    private final static String LANG_COOKIE_KEY = "language";

//    public I18nTool() {
//        try {
//            String lang = readLangCookie();
//            if (lang != null) {
//                log.info("lang cookie: " + lang);
//                storedLocale = new Locale(lang);
//            }
//        } catch (Exception e) {
//            log.severe(e.toString());
//        }
//    }

//    public void setStoredLocale(Locale storedLocale) {
//        this.storedLocale = storedLocale;
//        setLocale(storedLocale);
//
//    }

    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }


    /**
     * Calls particular methos depends on current i18n
     *
     * @param object     the object which has the i18n method
     * @param methodName the name of the method
     * @param argTypes   can be null
     * @param args       can be null
     * @return the result of the method
     */
    public String internationalizeMethod(Object object, String methodName, Class[] argTypes, Object[] args) throws Exception {
        if (methodName == null || object == null || methodName.isEmpty()) {
            String err = "One of input parameter is null: object=" + object + ", methodName" + methodName;
            log.warning(err);
            throw new IllegalArgumentException(err);
        }
        if ((argTypes != null && args == null) || (argTypes == null && args != null)) {
            String err = "One of input parameter is null: argTypes=" + argTypes + ", args=" + args;
            log.warning(err);
            throw new IllegalArgumentException(err);
        }
        Locale locale = getLocale();
        String lang = locale.getLanguage().toLowerCase();

       // int[] aa = new int [0]; aa[1]=2;//TODO

        String i18nMethodName = lang.equals("en") ? methodName : methodName + "_" + lang;

        Class clazz = object.getClass();
        Method method = null;
        boolean success = true;
        try {
            method = clazz.getMethod(i18nMethodName, argTypes);
        } catch (Exception e) {
            log.warning("Not found the i18n method " + i18nMethodName + " for " + clazz + ": " + e);
            //e.printStackTrace();
            success = false;
            try {
                method = clazz.getMethod(methodName, argTypes);
            } catch (NoSuchMethodException e1) {
                log.severe(e1.toString());
                //e1.printStackTrace();
                throw e1;

            }
        }

        String result = null;
        try {
            result = (String) method.invoke(object, args);
        } catch (Exception e2) {
            log.severe(e2.toString());
            //e2.printStackTrace();
        }

        if (result == null || result.isEmpty()) {
            log.warning("Method " + method.getName() + " of " + object + " returns an empty string");
            if (success) {
                try {
                    method = clazz.getMethod(methodName, argTypes);
                    result = (String) method.invoke(object, args);
                    if (result == null || result.isEmpty())
                        log.warning("Method " + method.getName() + " of " + object + " returns an empty string");
                } catch (Exception e) {
                    log.severe(e.toString());
                    //e.printStackTrace();
                    throw e;
                }

            }
        }


        return result;
    }

    /**
     * Returns a current site locale
     *
     * @return the locale
     */
    public Locale getLocale() {
//        if (storedLocale != null) {
//            log.info("getting a stored locale: " + storedLocale);
//            return storedLocale;
//        }

        FacesContext c = FacesContext.getCurrentInstance();
        Locale loc = null;
        if (c != null) {
            UIViewRoot vroot = c.getViewRoot();
            loc = vroot.getLocale();
        } else {
            log.warning("Faces context is null");
            if (currentLocale != null)
                loc = currentLocale;
            else
                loc = Locale.getDefault();
        }

        currentLocale = loc;
        log.info("currentLocale=" + currentLocale.toString());
        return loc;
    }

    /**
     * Changes a locale for a whole site
     *
     * @param locale the new locale
     * @throws Exception
     */
    public void setLocale(Locale locale) throws Exception {
        try {
            if (locale == null) {
                throw new IllegalArgumentException("Locale is null");
            }
            this.currentLocale = locale;
            FacesContext context = FacesContext.getCurrentInstance();
            UIViewRoot vroot = context.getViewRoot();
            vroot.setLocale(locale);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(getMessage("i18nTool.settingLocale"));
        }
    }

//    private String readLangCookie() {
//        // get cookies
//        HttpServletRequest httpServletRequest =
//                (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
//        Cookie[] cookies = httpServletRequest.getCookies();
//        if (cookies != null) {
//            for (int i = 0; i < cookies.length; i++) {
//                if (cookies[i].getName().equalsIgnoreCase(LANG_COOKIE_KEY)) {
//                    String cookieValue = cookies[i].getValue();
//                    return cookieValue;
//                }
//            }
//        }
//        return null;
//
//    }


    /**
     * Calls particular methos depends on current i18n. For example, for a method name
     * "someMethod" and current language "ru" will be called method "someMethod_ru".
     * If it returns null or there couses some exeption, then will be called "someMethod"
     *
     * @param object     an object which owns the method
     * @param methodName the name of the method
     * @return the result of the method invoking
     */
    public String internationalizeMethod(Object object, String methodName) throws Exception {
        return internationalizeMethod(object, methodName, null, null);
    }

    /**
     * Returns an i18n message from resource files
     *
     * @param key  the key of the message
     * @param args can be null
     * @return the i18n message
     */
    public String getMessage(String key, Object[] args) {
        return messageSource.getMessage(key, args, getLocale());
    }

    /**
     * @see kuzmenkov.oip.common.I18nTool#getMessage(String, Object[])
     */
    public String getMessage(String key) {
        return getMessage(key, null);
    }

}

